// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2022 Ivan Baidakou

#pragma once

#include "context.hpp"
#include "definitions.hpp"
#include "handler.hpp"
#include "messages.hpp"
#include <cassert>

namespace rotor_light {

struct SupervisorBase;
struct QueueBase;

struct ActorBase {
  using Backend = Handler<void (ActorBase::*)(Message &)>;
  using Storage = std::aligned_storage_t<sizeof(ActorBase::Backend),
                                         alignof(ActorBase::Backend)>;

  static constexpr auto backend_size = sizeof(Backend);
  static constexpr size_t min_handlers_amount = 1;

  ActorBase(char *backends, size_t backends_count);
  ActorBase(const ActorBase &) = delete;
  ActorBase(ActorBase &&) = delete;

  virtual void initialize();
  virtual uint8_t bind(ActorId initial_value, SupervisorBase *supervisor,
                       Context &context);
  inline ActorId get_id() const { return id; }
  inline State get_state() const { return state; }
  inline FailPolicy get_fail_policy() const { return fail_policy; }
  inline void set_fail_policy(FailPolicy value) { fail_policy = value; }

  void stop();
  bool add_event(Duration delta, Callback callback, void *data);

  template <typename MessageType, typename... Args>
  bool send(size_t queue_index, Args... args);

  template <typename Method> void subscribe(Method method) {
    using MethodHandler = Handler<Method>;
    static_assert(std::is_trivially_destructible_v<MethodHandler>,
                  "trivial destructor");
    assert(state == State::off);
    assert(backend_idx < (int)backends_count);
    auto ptr = backends_ptr + ++backend_idx * sizeof(Backend);
    new (ptr) MethodHandler(method);
  }

protected:
  friend struct SupervisorBase;
  void on_state_change(message::ChangeState &);

  virtual void advance_init();
  virtual void advance_start();
  virtual void advance_stop();

  ActorId id;
  ActorId mask;
  State state = State::off;
  SupervisorBase *supervisor;
  char *backends_ptr;
  size_t backends_count;
  int backend_idx;
  FailPolicy fail_policy;
};

template <size_t HandlersCount> struct Actor : ActorBase {
  static_assert(HandlersCount >= Actor::min_handlers_amount,
                "no enough handlers");
  Actor() : ActorBase(reinterpret_cast<char *>(&backends), HandlersCount) {}
  ActorBase::Storage backends[HandlersCount];
};

} // namespace rotor_light
